package pira.parser.xml.citeseerx;

import java.util.HashSet;
import java.util.Vector;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.IndexHits;

import pira.constants.PIRAConfig;
import pira.constants.PIRAConfig.MyRelationshipTypes;
import pira.db.ui.FullDBUI;
import pira.elements.Author;
import pira.elements.Paper;
import pira.utils.Log;

public class DBCopy {

	/**
	 * @param args
	 */
	private static String[] stop_words = { "", "abstract", "contents",
			"table of contents", "and", "references", "general terms",
			"acknowledgement[s]", "summary", "acknowledgment[s]", "disclaimer",
			"notes", "dyson 2002", "edited by", "index terms", "index",
			"article", "u s a", "approved", "revision history",
			"on information systems", "microsoft", "legal notices",
			"school working papers series", "environment", "results", "human",
			"published as", "wireless", "date of signature" };

	private FullDBUI oldDB;
	private FullDBUI newDB;

	private GraphDatabaseService oldService;
	private GraphDatabaseService newService;

	private Transaction oldTx;
	private Transaction newTx;

	private Log log;
	private Log error;
	
	private HashSet<String> errorTitles;

	private long paperNumber;
	private long authorNumber;
	
	private static long startTime;
	
	public DBCopy() {
		oldDB = new FullDBUI("fullDB_v4/");
		newDB = new FullDBUI("fullDB_v5/");
		oldService = oldDB.getGraphDatabaseService();
		newService = newDB.getGraphDatabaseService();
		paperNumber = 0;
		authorNumber = 0;
		startTime = System.currentTimeMillis();
		log = new Log("old_new_id.txt");
		log.write("OldID\tNewID\n");
		error = new Log("Copy_err_log.txt");
		errorTitles = new HashSet<String>();
		for (String title : stop_words)
			errorTitles.add(title);
	}

	public void copyAllNodes() {
		oldTx = oldService.beginTx();
		newTx = newService.beginTx();
		try {
			
			Node gnode = newService.createNode();
			gnode.setProperty(PIRAConfig.ALLNODENUMBER, 2);
			gnode.setProperty(PIRAConfig.PAPERNUMBER, 0);
			gnode.setProperty(PIRAConfig.AUTHORNUMBER, 0);
			
			Node externalNode = newService.createNode();
			externalNode.setProperty(Paper.TITLE, PIRAConfig.EXTERNAL);
			
			long paperCount = 0;
			for (Node paper : oldDB.papers.query(Paper.PAPERID, "*")) {
				paperCount++;
				boolean foundInDB = true;
				String title = FullDBUI.normal((String) paper.getProperty(Paper.TITLE));
				long newId = -1;
				if (!errorTitles.contains(title) && title.length() > 2) {
					IndexHits<Node> hits = newDB.papers.get(Paper.TITLE, title);
					// weather exist the paper with the same title
					if (hits.size() != 0) {
						// compare authors
						Vector<String> authors = new Vector<String>();
						Iterable<Relationship> rels = paper.getRelationships(
								MyRelationshipTypes.WROTE, Direction.INCOMING);
						for (Relationship r : rels) {
							authors.add(FullDBUI.normal((String) r.getStartNode().getProperty(
									Author.FULLNAME)));
						}

						for (Node node : hits) {
							int sameAuthorNb = 0;
							for (Relationship r : node.getRelationships(
									MyRelationshipTypes.WROTE,
									Direction.INCOMING)) {
								if (authors.contains(FullDBUI.normal((String) r.getStartNode()
										.getProperty(Author.FULLNAME)))) {
									sameAuthorNb++;
								}
							}

							if (authors.size() >= 2) {
								if (sameAuthorNb >= 2)
									foundInDB = true;
							} else {
								if (sameAuthorNb >= 1)
									foundInDB = true;
							}
							
							if (foundInDB) {
								newId = node.getId();
								break;
							}
						}

					} else {
						foundInDB = false;
					}
				} else {
					foundInDB = true;
				}

				if (!foundInDB) {
					newId = copyNode(paper);
				}
				
				paper.setProperty("new id", newId);
				log.write(paper.getId() + "\t" + newId + "\n");
				
				if (paperCount % 10000 == 0) {
					long timeElapsed = (System.currentTimeMillis() - startTime)/1000;
					System.out.println(paperCount + "\t" + timeElapsed + "s\t" + (paperCount * 100 / 1388883) + "%.");
					Node gNode = newService.getNodeById(PIRAConfig.GLOBALNODEID);
					gNode.setProperty(PIRAConfig.AUTHORNUMBER, authorNumber);
					gNode.setProperty(PIRAConfig.PAPERNUMBER, paperNumber);
					gNode.setProperty(PIRAConfig.ALLNODENUMBER, authorNumber + paperNumber
							+ 2);
					
					oldTx.success();
					oldTx.finish();
					oldTx = oldService.beginTx();
					newTx.success();
					newTx.finish();
					newTx = newService.beginTx();
				}
			}
			oldTx.success();
			newTx.success();
		} finally {
			oldTx.finish();
			newTx.finish();
		}
	}

	private long copyNode(Node paper) {
		Node node = newService.createNode();
		
		this.paperNumber++;
		String paperid = (String) paper.getProperty(Paper.PAPERID);
		node.setProperty(Paper.PAPERID, paperid);
		newDB.papers.add(node, Paper.PAPERID, paperid);
		
		if (paper.hasProperty(Paper.CLUSTERID)) {
			String clusterid = null;
			try {
				clusterid = (String) paper.getProperty(Paper.CLUSTERID);
			} catch (ClassCastException e) {
				clusterid = "" + paper.getProperty(Paper.CLUSTERID);
				error.write(paper.getId() + " error : clusterid = " + paper.getProperty(Paper.CLUSTERID) + "\n");
			}
			node.setProperty(Paper.CLUSTERID, clusterid);
			newDB.papers.add(node, Paper.CLUSTERID, clusterid);
		}
		
		String title = (String) paper.getProperty(Paper.TITLE);
		node.setProperty(Paper.TITLE, title);
		newDB.papers.add(node, Paper.TITLE, FullDBUI.normal(title));
		
		for (Relationship r : paper.getRelationships(MyRelationshipTypes.WROTE, Direction.INCOMING)) {
			Node authorNode = r.getStartNode();
			String authorid = (String) authorNode.getProperty(Author.AUTHORID);
			String fullName = (String) authorNode.getProperty(Author.FULLNAME);
			Author author = new Author();
			author.setAuthorID(authorid);
			author.setFullName(fullName);
			
			Node newAuthorNode = addAuthorToDB(author);
			// "wrote" and "written_by" relationships
			newAuthorNode.createRelationshipTo(node, MyRelationshipTypes.WROTE);
			node.createRelationshipTo(newAuthorNode, MyRelationshipTypes.WRITTEN_BY);
		}
		return node.getId();
	}

	// add current author and "wrote" relation to DB
	private Node addAuthorToDB(Author author) {
		Node node = null;
		// judge weather the author already exist in database
		// get the author id of the author
		String authorID = author.getAuthorID();
		if (authorID.equals(PIRAConfig.NULL))
			authorID = ""
					+ FullDBUI.normal(author.getFullName()).hashCode();

		IndexHits<Node> hits = newDB.authors.get(Author.AUTHORID, authorID);
		// if found in database
		if (hits.size() > 0)
			node = hits.getSingle();
		else {
			// if not found , create new node
			node = newService.createNode();

			// author number increase
			this.authorNumber++;

			// properties
			node.setProperty(Author.AUTHORID, authorID);
			node.setProperty(Author.FULLNAME, author.getFullName());

			// use author id as index
			newDB.authors.add(node, Author.AUTHORID, authorID);
		}
		return node;
	}

	public void copyAllCitations() {
		newTx = newService.beginTx();
		try {
			long count = 0;
			for (Node oldNode : oldDB.papers.query(Paper.PAPERID, "*")) {
				long currentNewId = (Long) oldNode.getProperty("new id");
				count++;
				if (currentNewId != -1) {
					Node currentNewNode = newService.getNodeById(currentNewId);
					for (Relationship r : oldNode.getRelationships(MyRelationshipTypes.CITE, Direction.OUTGOING)) {
						long endNewId = (Long) r.getEndNode().getProperty("new id");
						if (endNewId != -1) {
							Node endNewNode = newService.getNodeById(endNewId);
							currentNewNode.createRelationshipTo(endNewNode, MyRelationshipTypes.CITE);
						}
					}
				}
				
				if (count % 10000 == 0) {
					System.out.println(count);
					newTx.success();
					newTx.finish();
					newTx = newService.beginTx();
				}
			}
			newTx.success();
		} finally {
			newTx.finish();
		}
	}
	
	private void shutDown() {
		log.close();
		error.close();
		oldDB.shutDown();
		newDB.shutDown();
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DBCopy copy = new DBCopy();
//		copy.copyAllNodes();
		copy.copyAllCitations();
		copy.shutDown();
	}

}
